/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package ia_psr.algoritmos;

/**
 *
 * @author Rubén Avalos Elvira y Fabiana Celiberti Tapie
 */
import ia_psr.problemas.IProblema;
import ia_psr.problemas.Variable;
import ia_psr.graph.Arista;
import ia_psr.graph.Nodo;
import ia_psr.graph.NodoPSR;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;

public class PSRConsistenciaArco extends PSRMinimoValorRestante {
    protected boolean inconsistenciaArco;

    public PSRConsistenciaArco() {
        super();
    }

    // Eleccion simplista de siguiente nodo
    public NodoPSR nextNode() {
        for(Nodo nodo : g.getNodos()) {
            NodoPSR n = (NodoPSR)nodo;
            if (n.valor.getValue() == null) {
                return n;
            }
        }
        return null;
    }

    protected synchronized boolean iiResolver(Variable v, ArrayList< LinkedList<Variable> > t) {
        if (solucion)
            return true;
        int resultadoComprobacion = g.estadoValido();
        if (resultadoComprobacion == IProblema.VALIDO) {
            solucion = true;
            return true;
        }
        if (resultadoComprobacion == IProblema.NO_VALIDO)
            return false;

        Collections.copy(tabla, t); // nextNode usa tabla
        NodoPSR n = nextNode();// Aplicacion de la heuristica 1
        ++nodosExpandidos;

        boolean resultado;
        int nodeIndex = g.getNodos().indexOf(n);
        LinkedList<Variable> listaVariables = t.get(nodeIndex);

        Variable var;
        for (int i = 0; i < listaVariables.size(); ++i) {
            ++variablesExpandidas;
            var = listaVariables.get(i);
            g.asignVariable(n, var);
            ArrayList<LinkedList<Variable> > taux;
            taux = cloneTable(t);

            // Poner que el dominio del nodo n solo es la variable var
            LinkedList<Variable> dominioAsignado = new LinkedList<Variable>();
            dominioAsignado.add(var);
            taux.set(nodeIndex, dominioAsignado);

            Collections.copy(tabla, taux);

            AC3();
            if (inconsistenciaArco) {
                g.resetNodo(n);
                continue;
            }
            taux = cloneTable(tabla);
            resultado = iiResolver(var, taux);
            if (resultado)
                return true;
            else {
                g.resetNodo(n);
            }
        }
        return false;
    }

    public synchronized boolean iResolver(Variable v) {
        return iiResolver(v, tabla);
    }

    protected synchronized void AC3() {
        inconsistenciaArco = false;
        LinkedList<Arista> cola = new LinkedList<Arista>();
        for (Nodo n : g.getNodos()) {
            for (Arista a : n.getAristas()) {
                    cola.offerLast(a);
            }
        }
        while (!cola.isEmpty()) {
            Arista a = cola.pollFirst();
            Nodo Xi = a.origen;

            if (borrarValoresInconsistentes(a)) {
                for (Arista ai : Xi.getAristas())
                    cola.offerLast(ai);
            }
        }
    }

    public boolean borrarValoresInconsistentes(Arista a) {
        boolean borrado = false;

        Nodo Xi = a.origen;
        Nodo Xj = a.destino;
        int indexXi = g.getNodos().indexOf(Xi);
        int indexXj = g.getNodos().indexOf(Xj);

        LinkedList<Variable> orig_dominioXi = tabla.get(indexXi);
        LinkedList<Variable> dominioXj = tabla.get(indexXj);

        LinkedList<Variable> dominioXi = (LinkedList<Variable>)tabla.get(indexXi).clone();

        for (Variable x : dominioXi) {

            if(!arcoConsistente(x, dominioXj)) {
                orig_dominioXi.remove(x);
                borrado = true;
            }
         }

        if (orig_dominioXi.isEmpty()) {
            inconsistenciaArco = true;
        }

        return borrado;
    }

    protected boolean arcoConsistente(Variable x, LinkedList<Variable> dXj) {
        boolean resultado = false;
        for (Variable y : dXj) {
            if (!x.equals(y))
                resultado = true;
        }
        return resultado;
    }

}
